读完了胡子大哈的React.js 小书后,总结一下对 redux 的新理解吧,主要是 store,connect,reducer,provider 等内容的一些浅见。

redux 的出现,事实上为了解决状态管理的问题。最直接的问题就是,如何在各个组件之间共享状态(即组件间通讯)。

Provider

redux 应对上述问题的解决方案,就是将这些需要共享的状态放置在根组件的context中(类似于全局变量),其子组件都能获取到 context。

具体的做法,其实是将 Index 组件(根组件)包装成 Provider 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export class Provider extends Component {
static propTypes = {
store: PropTypes.object,
children: PropTypes.any,
};

static childContextTypes = {
store: PropTypes.object,
};

getChildContext() {
return {
store: this.props.store,
};
}

render() {
return <div>{this.props.children}</div>;
}
}

这个 Provider 组件,为整个组件树添加了 store,任何子组件都能获取到 store

1
2
3
<Provider store={store}>
<Index />
</Provider>

Store

store 算是组件的状态管理器,也是整个 redux 的核心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createStore(reducer) {
let state = null;
const listeners = [];
const subscribe = (listener) => listeners.push(listener);
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach((listener) => listener());
};
dispatch({});
return {
getState,
dispatch,
subscribe,
};
}

store 做的事情包括以下三件:

  1. getState:用于获取当前的状态
  2. dispatch:修改状态(reducer),并将事件分发给所有监听器
  3. subscribe:订阅事件,用它来增加新的监听器

Reducer

reducer 由用户自己定义,有两个参数,前者是老状态,后者是一个 action。

它做的事情仅仅是:初始化和计算新的 state;根据 action 的信息,reducer 修改对应的状态,并返回一个新状态(全新的对象,而不是在老对象上修改)。

举个书中的例子:

1
2
3
4
5
6
7
8
9
10
11
12
const themeReducer = (state, action) => {
if (!state)
return {
themeColor: "red",
};
switch (action.type) {
case "CHANGE_COLOR":
return { ...state, themeColor: action.themeColor };
default:
return state;
}
};

reducer,按照规定,应该是一个纯函数

Connect

connect 从本质上讲,是一个高阶组件,它的作用可以理解为:

  1. 子组件通过 connect 成为高阶组件后,其内部不用再去对 context 进行读写(降低依赖性)
  2. 经过 connect 后,从 context 的 store 里面读取到的 state/dispatch/props,将会统一以 props 的状态传入子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
export const connect = (mapStateToProps, mapDispatchToProps) => (
WrappedComponent
) => {
class Connect extends Component {
static contextTypes = {
store: PropTypes.object,
};

constructor() {
super();
this.state = {
allProps: {},
};
}

_updateProps() {
const { store } = this.context;
let stateProps = mapStateToProps
? mapStateToProps(store.getState(), this.props)
: {}; // 防止 mapStateToProps 没有传入
let dispatchProps = mapDispatchToProps
? mapDispatchToProps(store.dispatch, this.props)
: {}; // 防止 mapDispatchToProps 没有传入
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props,
},
});
}

componentWillMount() {
const { store } = this.context;
this._updateProps();
store.subscribe(() => this._updateProps());
}

render() {
return <WrappedComponent {...this.state.allProps} />;
}
}

return Connect;
};